package device

import (
	"context"
	"errors"
	"fmt"
	"github.com/wj008/goyee/config"
	"net"
	"sync"
	"time"
)

// UDPClient UDP客户端设备对象
type UDPClient struct {
	sync.Mutex
	serverAddr     string
	localAddr      string
	readTimeout    time.Duration
	writeTimeout   time.Duration
	reconnectDelay time.Duration
	chSend         chan []byte
	chSendOpen     bool
	isRunning      bool
	callback       DataCallback
	conn           *net.UDPConn
}

// NewUDPClient 创建新的UDP客户端实例，接收具体参数
func NewUDPClient(serverAddr, localAddr string, readTimeout, writeTimeout, reconnectDelay time.Duration) *UDPClient {
	return &UDPClient{
		serverAddr:     serverAddr,
		localAddr:      localAddr,
		readTimeout:    readTimeout,
		writeTimeout:   writeTimeout,
		reconnectDelay: reconnectDelay,
	}
}

// NewUDPClientDefault 创建使用默认配置的UDP客户端实例
func NewUDPClientDefault() *UDPClient {
	address := config.String("udp_addr", "localhost:8080")
	readTimeout := config.Int("udp_read_timeout", 3)
	writeTimeout := config.Int("udp_write_timeout", 3)
	reconnectDelay := config.Int("udp_reconnect_delay", 5)
	return NewUDPClient(
		address,
		"",
		time.Duration(readTimeout)*time.Second,
		time.Duration(writeTimeout)*time.Second,
		time.Duration(reconnectDelay)*time.Second,
	)
}

// Run 启动UDP客户端服务，包含自动重连机制
func (uc *UDPClient) Run(ctx context.Context, cb DataCallback) {
	uc.callback = cb
	timer := 1
	for {
		select {
		case <-ctx.Done():
			fmt.Printf("UDP客户端 %s: 服务被终止\n", uc.serverAddr)
			uc.Stop()
			return
		case <-time.After(time.Duration(timer) * time.Second):
			timer = 5 // 重连间隔，第一次1秒，之后5秒
			fmt.Printf("UDP客户端 %s: 尝试启动服务\n", uc.serverAddr)
			if err := uc.setup(ctx); err != nil {
				fmt.Printf("UDP客户端 %s: 启动失败: %v\n", uc.serverAddr, err)
			}
		}
	}
}

// Stop 停止UDP客户端服务
func (uc *UDPClient) Stop() {
	uc.Lock()
	defer uc.Unlock()
	uc.isRunning = false
	if uc.conn != nil {
		_ = uc.conn.Close()
	}
	if uc.chSendOpen {
		close(uc.chSend)
		uc.chSendOpen = false
	}
}

// setup 初始化UDP连接，使用子上下文管理
func (uc *UDPClient) setup(parentCtx context.Context) error {
	// 创建子上下文，用于管理当前setup的生命周期
	uc.Lock()
	uc.chSend = make(chan []byte, 10)
	uc.chSendOpen = true
	uc.Unlock()

	ctx, cancel := context.WithCancel(parentCtx)
	defer cancel() // 确保退出时取消子上下文

	// 解析服务器地址
	serverUDPAddr, err := net.ResolveUDPAddr("udp", uc.serverAddr)
	if err != nil {
		return fmt.Errorf("解析服务器地址失败: %w", err)
	}

	// 解析本地地址（可选）
	var localUDPAddr *net.UDPAddr
	if uc.localAddr != "" {
		localUDPAddr, err = net.ResolveUDPAddr("udp", uc.localAddr)
		if err != nil {
			return fmt.Errorf("解析本地地址失败: %w", err)
		}
	}

	// 创建UDP连接
	conn, err := net.DialUDP("udp", localUDPAddr, serverUDPAddr)
	if err != nil {
		return fmt.Errorf("创建UDP连接失败: %w", err)
	}

	fmt.Printf("UDP客户端 %s: 启动成功\n", uc.serverAddr)
	uc.Lock()
	uc.conn = conn
	uc.isRunning = true
	uc.Unlock()

	// 用于接收子协程的错误
	errChan := make(chan error, 2)

	// 启动读取协程
	go func() {
		err := uc.readFromConn(ctx)
		if err != nil {
			errChan <- fmt.Errorf("读取协程错误: %w", err)
		}
	}()

	// 启动写入处理协程
	go func() {
		err := uc.processWrites(ctx)
		if err != nil {
			errChan <- fmt.Errorf("写入协程错误: %w", err)
		}
	}()

	// 监控子上下文和错误通道
	select {
	case <-ctx.Done():
		// 父上下文被取消
		uc.Stop()
		return ctx.Err()
	case err := <-errChan:
		uc.Stop()
		return err
	}
}

// readFromConn 从UDP连接读取数据，发生错误时返回
func (uc *UDPClient) readFromConn(ctx context.Context) error {
	buf := make([]byte, 1024)
	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		default:
			// 设置读取超时
			if uc.readTimeout > 0 {
				if err := uc.conn.SetReadDeadline(time.Now().Add(uc.readTimeout)); err != nil {
					return fmt.Errorf("设置读取超时失败: %w", err)
				}
			}

			// 读取数据
			n, remoteAddr, err := uc.conn.ReadFromUDP(buf)
			if err != nil {
				// 检查是否是超时错误
				if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
					continue
				}
				return fmt.Errorf("读取数据失败: %w", err)
			}

			if n > 0 && uc.callback != nil {
				// 调用回调函数处理接收到的数据
				uc.callback(buf[:n], remoteAddr.String())
			}
		}
	}
}

// processWrites 处理发送数据，发生错误时返回
func (uc *UDPClient) processWrites(ctx context.Context) error {
	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case data := <-uc.chSend:
			// 设置写入超时
			if uc.writeTimeout > 0 {
				if err := uc.conn.SetWriteDeadline(time.Now().Add(uc.writeTimeout)); err != nil {
					return fmt.Errorf("设置写入超时失败: %w", err)
				}
			}

			// 发送数据
			if _, err := uc.conn.Write(data); err != nil {
				// 返回写入错误，会被setup函数捕获
				return fmt.Errorf("写入数据失败: %w", err)
			}
		}
	}
}

// SendData 发送数据到UDP服务器
func (uc *UDPClient) SendData(data []byte) error {
	uc.Lock()
	defer uc.Unlock()

	if !uc.isRunning || !uc.chSendOpen {
		return errors.New("UDP客户端未运行")
	}

	select {
	case uc.chSend <- data:
		return nil
	case <-time.After(1 * time.Second):
		return errors.New("发送数据超时")
	}
}

// GetLocalAddr 获取本地地址
func (uc *UDPClient) GetLocalAddr() string {
	uc.Lock()
	defer uc.Unlock()

	if uc.conn != nil {
		return uc.conn.LocalAddr().String()
	}
	return ""
}

// GetRemoteAddr 获取远程服务器地址
func (uc *UDPClient) GetRemoteAddr() string {
	return uc.serverAddr
}

// IsRunning 检查客户端是否正在运行
func (uc *UDPClient) IsRunning() bool {
	uc.Lock()
	defer uc.Unlock()
	return uc.isRunning
}
